
#include "EGraphics.h"
#include "EUtil.h"
#include "ERuntime.h"

void apply_surface ( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
	SDL_Rect offset;
	offset.x = x;
	offset.y = y;
	SDL_BlitSurface ( source, NULL, destination, &offset );
}

namespace Eyas3D
{
	/* -------------------------------------------------------------------------- */
	EBitmap::EBitmap( int w, int h, int bpp ): name( "noname" ), pixels( NULL), pitch(0), width( w), height( h), bpp( bpp ), valid( true ), action( create )
	{
		int depth = 8*bpp;
		surface = SDL_CreateRGBSurface ( 0, w, h, depth, 0, 0, 0, 0 );
	}
	bool EBitmap::save( const EString &filename, bool overwrite )
	{
		if( surface!=NULL ){
			SDL_SaveBMP( surface, filename.c_str() );
		}
	}
	EBitmap::EBitmap( const EString &filename ) : name( filename ), pixels( NULL ),
		pitch( 0 ), width( 0 ), height( 0 ), valid( false ), action( load )
	{
		surface = SDL_LoadBMP( GetPath( filename ).c_str() );
		//surface = SDL_LoadBMP( ( filename ).c_str() );
		if( surface == NULL ){
			printf( "文件不存在, 错误是: %s\n", SDL_GetError() );
			exit( -1 );
		}
		width	= surface->w;
		height	= surface->h;
		pitch	= surface->pitch;
		valid	= true;
	}
	EBitmap::~EBitmap()
	{
		SDL_FreeSurface( surface );
	}
	bool EBitmap::setPixel( EInt x, EInt y, EColor c )
	{
		Uint32 pixel = c.ToInt();
		return setPixel ( x, y, pixel );
	}
	bool EBitmap::setPixel ( int x, int y, Uint32 pixel )
	{
		if( x<0||x>width || y<0||y>height )
			return false;
		int bpp = surface->format->BytesPerPixel;
		/* Here p is the address to the pixel we want to set */
		Uint8 *p = ( Uint8 * ) surface->pixels + y * surface->pitch + x * bpp;
		switch ( bpp ) {
		case 1:
			*p = pixel;
			break;
		case 2:
			* ( Uint16 * ) p = pixel;
			break;
		case 3:
			if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {
				p[0] = ( pixel >> 16 ) & 0xff;
				p[1] = ( pixel >> 8 ) & 0xff;
				p[2] = pixel & 0xff;
			} else {
				p[0] = pixel & 0xff;
				p[1] = ( pixel >> 8 ) & 0xff;
				p[2] = ( pixel >> 16 ) & 0xff;
			}
			break;
		case 4:
			* ( Uint32 * ) p = pixel;
			break;
		}
		return true;
	}
	
	bool EBitmap::getPixel( EInt x, EInt y, EColor *c )
	{
		//SDL_LockSurface
		//SDL_UnLockSurface
		//SDL_MUSTLOCK
		Uint32 color;
		bool retval = EBitmap::getPixel( x, y, &color );
		//void EColor::fill( UInt32 color, EColor *c );
		//保留(ff)0000这段数据
		c->r = (unsigned char)((color & 0xff0000) >> 16);
		c->g = (unsigned char)((color & 0x00ff00) >> 8);
		c->b = (unsigned char)(color & 0x0000ff);
		c->a = 255;
		return retval;
	}
	bool EBitmap::getPixel( EInt x, EInt y, Uint32 *c )
	{
		int bpp = surface->format->BytesPerPixel;
		if( x<0||x>width || y<0||y>height )
			return false;
		/* Here p is the address to the pixel we want to retrieve */
		Uint8 *p = ( Uint8 * ) surface->pixels + y * surface->pitch + x * bpp;
		switch ( bpp ) {
		case 1:
			//返回八位
			//一个像素一个字节
			*c = *p;
			return true;
			break;
		case 2:
			//一个像素两个字节
			*c = * ( Uint16 * ) p;
			return true;
			break;
		case 3:
			if ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
				*c = p[0] << 16 | p[1] << 8 | p[2];
			else
				*c = p[0] | p[1] << 8 | p[2] << 16;
			return true;
			break;
		case 4:
			*c = * ( Uint32 * ) p;
			return false;
			break;
		default:
			return false;       /* shouldn't happen, but avoids warnings */
		}
	}
	void EBitmap::info(){
		if( !valid ){
			cout<<"bitmap not valid"<<endl;
			return;
		}
		printf( "BitsPerPixel: %d\n", surface->format->BitsPerPixel );
		cout<<"BitsPerPixel: "<<surface->format->BitsPerPixel<<endl;
		cout<<"width: "<<width<<endl;
		cout<<"height: "<<height<<endl;
	}
	void EBitmap::viewer(){
		//view the picture on a window
	}
	/*
	EColor EBitmap::getPixel( EInt x, EInt y )	{
		//return EColor( surface->pixels[ y* getWidth() + x ], surface->pixels[ y* getWidth() + x +1 ], surface->pixels[ y* getWidth() + x + 2 ]  );
		//return pixels[ y*getWidth()+x ];
	}*/
	EGraphics *EGraphics::graphic;
/////////////////////////////////////////////////////////////////////////////////////////////////
	bool EGraphics::initGraphics( SDL_Window* window )
	{		
		//
		this->window = window;
		return initGraphics();
	}
	bool EGraphics::initGraphics()
	{
		EGraphics::graphic = this;
		TTF_Init();		
		//配合绝对路径的使用，ttf_openFont竟然出现了		
		//char fontpath[256];
		//ERuntime::runtime->separator
		/*
		if( ERuntime::runtime->system==Windows )
			font  = TTF_OpenFont( strcat( ERuntime::runtime->runTimePath, "\\simhei.ttf" ), 20 );
		if( ERuntime::runtime->system==Linux )
			font  = TTF_OpenFont( strcat( ERuntime::runtime->runTimePath, "/simhei.ttf" ), 20 );*/
		font  = TTF_OpenFont( "simhei.ttf" , 20 );
		if( font==NULL ){
			cout<<( "open font error, " )<<TTF_GetError()<<endl;
		}
		if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
		{
			SDL_Quit();
			//return false;//
			cout<<"SDL_Init error!" <<endl;
			exit( -1 );
		}
		//create the main surface
		//surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_SWSURFACE||SDL_HWSURFACE );
		surface = SDL_CreateRGBSurface ( 0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 0, 0, 0 );
		GZBuffer = new EFloat[ SCREEN_WIDTH * SCREEN_HEIGHT ];
		memset( GZBuffer, 0, sizeof(EFloat) * SCREEN_WIDTH * SCREEN_HEIGHT );
		return(true);
	}
	void EGraphics::shutdownGraphics()
	{
		TTF_CloseFont( font );
		TTF_Quit();
		SDL_FreeSurface( surface );
		free( GZBuffer );
		SDL_Quit();
	}
	void EGraphics::clearBuffer( const EColor &c )
	{
		//::FillRect( GBufferedHDC, &GBufferSize, GBgBrush );
		//刷成红色
		//SDL_FillRect ( surface, NULL, SDL_MapRGB ( surface->format, 255, 0, 0 ) );
		SDL_FillRect ( surface, NULL, SDL_MapRGB ( surface->format, c.r, c.g, c.b ) );
		::memset( GZBuffer, 0, sizeof(EFloat) * SCREEN_WIDTH * SCREEN_HEIGHT );
	}
	void EGraphics::draw_point ( int x, int y, Uint32 color )
	{
		if ( SDL_MUSTLOCK ( surface ) ) {
			if ( SDL_LockSurface ( surface ) < 0 ) {
				fprintf ( stderr, "Can't lock screen: %s\n", SDL_GetError() );
				return;
			}
		}
		setPixel (  x, y, color );
		if ( SDL_MUSTLOCK ( surface ) ) {
			SDL_UnlockSurface ( surface );
		}
		/* Update just the part of the display that we've changed */
		//SDL_UpdateRect ( surface, x, y, 1, 1 );
		//SDL_Flip( surface );
		//SDL_RenderPresent();
		SDL_UpdateWindowSurface( window );
		return;
	}
	void EGraphics::drawLine( EInt x0, EInt y0, EInt x1, EInt y1, const EColor &c )
	{
		Uint32 color = c.ToInt();
		drawLine( x0, y0, x1, y1, color );
	}
	void EGraphics::drawLine ( int x1, int y1, int x2, int y2, Uint32 c )
	{
		int dx, dy, x, y, p, const1, const2, inc, tmp;
		dx = x2 - x1;
		dy = y2 - y1;
		if ( dx * dy >= 0 )
			inc = 1;
		else
			inc = -1;
		if ( abs ( dx ) > abs ( dy ) ) {
			if ( dx < 0 ) {
				tmp = x1;
				x1 = x2;
				x2 = tmp;
				tmp = y1;
				y1 = y2;
				y2 = tmp;
				dx = -dx;
				dy = -dy;
			}
			p = 2 * dy - dx;
			const1 = 2 * dy;
			const2 = 2 * ( dy - dx );
			x = x1;
			y = y1;
			setPixel ( x, y, c );
			while ( x < x2 ) {
				x++;
				if ( p < 0 )
					p += const1;
				else {
					y += inc;
					p += const2;
				}
				setPixel ( x, y, c );
			}
		} else {
			if ( dy < 0 ) {
				tmp = x1;
				x1 = x2;
				x2 = tmp;
				tmp = y1;
				y1 = y2;
				y2 = tmp;
				dx = -dx;
				dy = -dy;
			}
			p = 2 * dy - dx;
			const1 = 2 * dy;
			const2 = 2 * ( dy - dx );
			x = x1;
			y = y1;
			setPixel ( x, y, c );
			while ( y < y2 ) {
				y++;
				if ( p < 0 )
					p += const1;
				else {
					x += inc;
					p += const2;
				}
				setPixel ( x, y, c );
			}
		}
	}
	#include "SDL_ttf.h"
	void EGraphics::drawString( const EString &str, EInt x, EInt y, const EColor &c )
	{
		//string to utf8
		//string to unicode
		if( font==NULL ){
			//cout<<( "open font error, " )<<TTF_GetError()<<endl;
			//printf("TTF_OpenFont: %s\n", TTF_GetError());
			return;
		}
		//TTF_RenderUTF8_Solid, TTF_RenderUNICODE_Solid
		SDL_Surface *gpChinese = TTF_RenderUTF8_Solid( font, str.c_str(), { c.r, c.g, c.b } );
		apply_surface( x, y, gpChinese, surface );
	}
	void EGraphics::fillTriangle( EInt x0, EInt y0, EInt x1, EInt y1, EInt x2, EInt y2, const EColor &c )
	{
		
	}
	void EGraphics::enableSmoothingMode( EBool enable )
	{
	}
	EBool EGraphics::checkZ( EInt x, EInt y, EFloat z )
	{
		EInt	index	= y * SCREEN_WIDTH + x;
		EFloat	divZ	= 1.0f / z;
		/* 这里是基于1/z做的比较 */
		if ( GZBuffer[index] > divZ )
			return(false);
		GZBuffer[index] = divZ;
		return(true);
	}
	bool EGraphics::setPixel ( int x, int y, Uint32 pixel )
	{
		int bpp = surface->format->BytesPerPixel;
		/* Here p is the address to the pixel we want to set */
		Uint8 *p = ( Uint8 * ) surface->pixels + y * surface->pitch + x * bpp;
		switch ( bpp ) {
		case 1:
			*p = pixel;
			break;
		case 2:
			* ( Uint16 * ) p = pixel;
			break;
		case 3:
			if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {
				p[0] = ( pixel >> 16 ) & 0xff;
				p[1] = ( pixel >> 8 ) & 0xff;
				p[2] = pixel & 0xff;
			} else {
				p[0] = pixel & 0xff;
				p[1] = ( pixel >> 8 ) & 0xff;
				p[2] = ( pixel >> 16 ) & 0xff;
			}
			break;
		case 4:
			* ( Uint32 * ) p = pixel;
			break;
		}
	}
	bool EGraphics::setPixel( EInt x, EInt y, /*EFloat z, */ const EColor &c )
	{
		Uint32 color = c.ToInt();
		setPixel( x, y, color );
	}
	void EGraphics::plot_circle_points ( int xc, int yc, int x, int y, Uint32 c )
	{
		setPixel ( xc + x, yc + y, c );
		setPixel ( xc - x, yc + y, c );
		setPixel ( xc + x, yc - y, c );
		setPixel ( xc - x, yc - y, c );
		setPixel ( xc + y, yc + x, c );
		setPixel ( xc - y, yc + x, c );
		setPixel ( xc + y, yc - x, c );
		setPixel ( xc - y, yc - x, c );
	}
	bool EGraphics::getPixel( EInt x, EInt y, EColor *c )
	{
		//Uint32 c;
		getPixel( x, y, c );
		//color.r
	}
	bool EGraphics::getPixel( EInt x, EInt y, Uint32 *c )
	{
		int bpp = surface->format->BytesPerPixel;
		/* Here p is the address to the pixel we want to retrieve */
		Uint8 *p = ( Uint8 * ) surface->pixels + y * surface->pitch + x * bpp;
		switch ( bpp ) {
		case 1:
			return *p;
		case 2:
			return * ( Uint16 * ) p;
		case 3:
			if ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
				return p[0] << 16 | p[1] << 8 | p[2];
			else
				return p[0] | p[1] << 8 | p[2] << 16;
		case 4:
			return * ( Uint32 * ) p;
		default:
			return 0;       /* shouldn't happen, but avoids warnings */
		}
	}
	void EGraphics::fillBuffer( SDL_Window* window )
	{
		//::BitBlt( hdc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GBufferedHDC, 0, 0, SRCCOPY );
		//window存根
		//SDL_Surface* SDL_GetWindowSurface(SDL_Window* window)
		//int SDL_UpdateWindowSurface(SDL_Window* window
		//flip
		SDL_Surface *wsurface = SDL_GetWindowSurface( window );
		SDL_BlitSurface( surface, NULL, wsurface, NULL );
		SDL_UpdateWindowSurface( window );
	}
	void EGraphics::fillBuffer()
	{
		if( window == NULL ){
			cout<<"debug: window == NULL"<<endl;
			exit( -1 );
		}
		SDL_Surface *wsurface = SDL_GetWindowSurface( window );
		SDL_BlitSurface( surface, NULL, wsurface, NULL );
		SDL_UpdateWindowSurface( window );
	}
	/*
	 * InnerSurface_drawTriangles
	 * InnerSurface_drawLine
	 * InnerSurface_drawPixel
	 * InnerSurface_drawString
	 * surfaceUpdate2Window
	 */
}
